#include "TCPGameManager.h"

TCPGameManager::TCPGameManager(QObject *parent, QTcpSocket * clientSocket, ConnectFour::Piece piece, ChatWidget const * chatWidget) :
    GameManager(parent) {
    socket = clientSocket;
    startingPiece = ConnectFour::BLACK;
    playerPiece = piece;
    queueOpponentMove = Animation::NONE;

    connectSignalSlots(chatWidget);

    emit connectingNew();
}

void TCPGameManager::connectSignalSlots(ChatWidget const * chatWidget) {
    connect(this, SIGNAL(winPiece(ConnectFour::Piece)), this, SLOT(determineWinner(ConnectFour::Piece)));
    connect(this, SIGNAL(connectingNew()), chatWidget, SLOT(newConnection()));
    connect(this, SIGNAL(newGameStarted()), chatWidget, SLOT(newGame()));
    connect(this, SIGNAL(socketMessageReceived(Network::SocketMessage)), chatWidget, SLOT(socketMessage(Network::SocketMessage)));
    connect(chatWidget, SIGNAL(responseSubmitted(Network::SocketMessage)), this, SLOT(sendResponse(Network::SocketMessage)));
    connect(this, SIGNAL(responseReceived(Network::SocketMessage)), chatWidget, SLOT(confirmRespond(Network::SocketMessage)));
    connect(this, SIGNAL(requested(Network::SocketMessage)), chatWidget, SLOT(userRequest(Network::SocketMessage)));
    connect(chatWidget, SIGNAL(chatSubmitted(QString)), this, SLOT(sendChat(QString)));
    connect(this, SIGNAL(chatReceived(QString)), chatWidget, SLOT(chatMessage(QString)));
    connect(this, SIGNAL(winPlayer(bool)), chatWidget, SLOT(playerWon(bool)));
    connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), chatWidget, SLOT(socketError(QAbstractSocket::SocketError)));
    connect(socket, SIGNAL(disconnected()), chatWidget, SLOT(socketDisconnected()));
    connect(socket, SIGNAL(readyRead()), this, SLOT(socketMessage()));
}

void TCPGameManager::newGame() {
    requestState = NONE;
    isAnimating = false;
    for (int i = 0; i < NUM_COLUMNS; i++) {
        columns[i].resetPieces();
    }
    for (int i = 0; i < NUM_WINS; i++) {
        wins[i].length = -1;
    }
    previousMoveIndex = -1;
    currentPlayer = startingPiece;
    if (startingPiece == ConnectFour::RED) {
        startingPiece = ConnectFour::BLACK;
    } else {
        startingPiece = ConnectFour::RED;
    }
    emit playerChanged(currentPlayer);
    emit newGameStarted();
}

void TCPGameManager::requestNewGame() {
    if (requestState == NONE && !isAnimating) {
        emit requested(Network::REQUEST_NEW_GAME);
        requestState = MAKING_REQUEST;
        socket->write(QByteArray::number(Network::REQUEST_NEW_GAME, BASE_CONVERSION));
    }
}

void TCPGameManager::requestMove(int index) {
    if (currentPlayer == playerPiece && !isAnimating && requestState == NONE) {
        if (!columns[index].isFull()) {
            socket->write(QByteArray::number(index, BASE_CONVERSION));
            columnAnimation(index);
        }
    }
}

void TCPGameManager::addPieceToColumn(int index) {
    isAnimating = false;
    if (currentPlayer == playerPiece) {
        columns[index].addPiece(playerPiece);
        if (playerPiece == ConnectFour::RED) {
            currentPlayer = ConnectFour::BLACK;
            emit playerChanged(ConnectFour::BLACK);
        } else {
            currentPlayer = ConnectFour::RED;
            emit playerChanged(ConnectFour::RED);
        }
        previousMoveIndex = index;
        emit columnNeedsUpdate(columns + index, index);
        checkWinConditions();
        checkDrawConditions();
    } else if (currentPlayer != playerPiece) {
        addOpponentPieceToColumn(index);
    }
}

void TCPGameManager::addOpponentPieceToColumn(int index) {
    columns[index].addPiece(currentPlayer);
    currentPlayer = playerPiece;
    emit playerChanged(playerPiece);
    previousOpponentMoveIndex = index;
    emit columnNeedsUpdate(columns + index, index);
    checkWinConditions();
    checkDrawConditions();
}

void TCPGameManager::requestUndo() {
    if (previousMoveIndex > -1 && requestState == NONE && !isAnimating) {
        emit requested(Network::REQUEST_UNDO);
        requestState = MAKING_REQUEST;
        socket->write(QByteArray::number(Network::REQUEST_UNDO, BASE_CONVERSION));
    }
}

void TCPGameManager::undoPlayerMove() {
    if (currentPlayer == playerPiece) {
        columns[previousOpponentMoveIndex].undoPiece();
        emit columnNeedsUpdate(columns + previousOpponentMoveIndex, previousOpponentMoveIndex);
        previousOpponentMoveIndex = -1;
    }
    columns[previousMoveIndex].undoPiece();
    emit columnNeedsUpdate(columns + previousMoveIndex, previousMoveIndex);
    previousMoveIndex = -1;
    currentPlayer = playerPiece;
    emit playerChanged(playerPiece);
}

void TCPGameManager::undoOpponentMove() {
    if (currentPlayer != playerPiece) {
        columns[previousMoveIndex].undoPiece();
        emit columnNeedsUpdate(columns + previousMoveIndex, previousMoveIndex);
        previousMoveIndex = -1;
    }
    columns[previousOpponentMoveIndex].undoPiece();
    emit columnNeedsUpdate(columns + previousOpponentMoveIndex, previousOpponentMoveIndex);
    previousOpponentMoveIndex = -1;
    if (playerPiece == ConnectFour::RED) {
        currentPlayer = ConnectFour::BLACK;
    } else {
        currentPlayer = ConnectFour::RED;
    }
    emit playerChanged(currentPlayer);
}

void TCPGameManager::finishAnimation(Animation::AnimationMode mode) {
    switch (mode) {
    case Animation::CLEAR_ALL:
        newGame();
        break;
    default:
        addPieceToColumn((int) mode);
        break;
    }
    if (queueOpponentMove != Animation::NONE) {
        columnAnimation((int) queueOpponentMove);
        queueOpponentMove = Animation::NONE;
    }
}

void TCPGameManager::socketMessage() {
    Network::SocketMessage message = (Network::SocketMessage) socket->read(PACKET_SIZE).toInt(NULL, BASE_CONVERSION);
    if (message >= Network::REQUEST_NEW_GAME) {
        emit socketMessageReceived(message);
    }
    switch (message) {
    case Network::CHAT_INCOMING:
        emit chatReceived(QString::fromAscii(socket->readAll()));
        break;
    case Network::REQUEST_NEW_GAME:
        requestState = RECEIVED_REQUEST;
        break;
    case Network::ACCEPT_NEW_GAME:
        newGameAnimation();
    case Network::REJECT_NEW_GAME:
        requestState = NONE;
        break;
    case Network::REQUEST_UNDO:
        requestState = RECEIVED_REQUEST;
        break;
    case Network::ACCEPT_UNDO:
        undoPlayerMove();
    case Network::REJECT_UNDO:
        requestState = NONE;
        break;
    default:
        if (!isAnimating) {
            columnAnimation(message);
        } else {
            queueOpponentMove = (Animation::AnimationMode) message;
        }
        break;
    }
}

void TCPGameManager::sendResponse(Network::SocketMessage message) {
    if (requestState == RECEIVED_REQUEST) {
        socket->write(QByteArray::number(message, BASE_CONVERSION));
        emit responseReceived(message);
        switch (message) {
        case Network::ACCEPT_NEW_GAME:
            newGameAnimation();
            break;
        case Network::ACCEPT_UNDO:
            undoOpponentMove();
            break;
        default:
            break;
        }
        requestState = NONE;
    }
}

void TCPGameManager::sendChat(QString chat) {
    socket->write(QByteArray::number(Network::CHAT_INCOMING, BASE_CONVERSION).append(chat.toAscii()));
}

void TCPGameManager::determineWinner(ConnectFour::Piece piece) {
    emit winPlayer(piece == playerPiece);
}
